﻿using Devonline.Entity;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;

namespace Devonline.Communication.Server;

/// <summary>
/// 消息集线器
/// string 作为主键类型
/// </summary>
public class MessageServer : CommunicationServer<IMessageCommunicatorClient, Message>
{
    private const string LOG_INFO = "Server receive the {messageType} message from user {user} in client {client} to {receiver} with content: ";
    public MessageServer(
        ILogger<MessageServer> logger,
        IDistributedCache cache,
        IHttpContextAccessor httpContextAccessor,
        HostSetting hostSetting
        ) : base(logger, cache, httpContextAccessor, hostSetting) { }

    #region 供客户端调用的公开方法
    /// <summary>
    /// 服务器转发消息
    /// </summary>
    /// <param name="message"></param>
    /// <returns></returns>
    public virtual async Task<Message> Send(Message message)
    {
        try
        {
            InitMessage(message);
            await _cache.SetStringAsync(message.Id, message.ToJsonString());

            if (string.IsNullOrWhiteSpace(message.Receiver) && string.IsNullOrWhiteSpace(message.Group))
            {
                //发给所有人
                _logger.LogInformation(LOG_INFO + message.Content, message.Type, User, Client, "All");
                await Clients.AllExcept(new string[] { Context.ConnectionId }).Receive(message);
            }
            else if (string.IsNullOrWhiteSpace(message.Receiver) && (!string.IsNullOrWhiteSpace(message.Group)))
            {
                //发给组中的每个人
                _logger.LogInformation(LOG_INFO + message.Content, message.Type, User, Client, message.Group);
                //TODO, 组需要数据支持, 此处暂不实现
                //await base.SendAsync(message, message.Receiver);
            }
            else
            {
                //记录日志
                LogMessage(message);
                await base.SendAsync(message, message.Receiver);
            }

            //保存当前消息
            await SaveMessageAsync(message);
        }
        catch (Exception ex)
        {
            LogException(ex, message);
        }

        return message;
    }
    /// <summary>
    /// 服务器接收消息回执方法
    /// 设置原始消息的接收者和接收时间
    /// </summary>
    /// <param name="message">回执的消息</param>
    /// <returns></returns>
    public virtual async Task Ack(Message message)
    {
        try
        {
            var originalMessage = await GetFromCacheAsync(message.Id);
            originalMessage.To = message.From;
            originalMessage.ReceiveTime = DateTime.UtcNow;
            await _cache.SetStringAsync(message.Id, originalMessage.ToJsonString());

            //转发回执
            ArgumentNullException.ThrowIfNull(originalMessage.Sender);
            message.Type = MessageType.Ack;
            message.Content = originalMessage.Type.ToString();
            message.ReceiveTime = originalMessage.ReceiveTime;
            await Clients.Group(originalMessage.Sender).Ack(message);

            //记录日志
            LogMessage(message);

            //保存当前消息
            await SaveMessageAsync(originalMessage);
        }
        catch (Exception ex)
        {
            LogException(ex, nameof(Ack));
        }
    }
    /// <summary>
    /// 服务器接收消息已读方法
    /// 设置原始消息的已读时间
    /// </summary>
    /// <param name="message">已读的消息</param>
    /// <returns></returns>
    public virtual async Task Read(Message message)
    {
        try
        {
            var originalMessage = await GetFromCacheAsync(message.Id);
            originalMessage.ReadTime = DateTime.UtcNow;
            await _cache.SetStringAsync(message.Id, originalMessage.ToJsonString());

            //转发已读
            ArgumentNullException.ThrowIfNull(originalMessage.Sender);
            message.Type = MessageType.Read;
            message.Content = originalMessage.Type.ToString();
            message.ReadTime = originalMessage.ReadTime;
            await Clients.Group(originalMessage.Sender).Read(message);

            //记录日志
            LogMessage(message);

            //保存当前消息
            await SaveMessageAsync(originalMessage);
        }
        catch (Exception ex)
        {
            LogException(ex, nameof(Read));
        }
    }
    /// <summary>
    /// 强制客户端通讯器退出
    /// 创建并保存一个 Command 类型的新消息, 记录退出相关时间
    /// 退出命令只有离线方法中查询数据库才能找到并设置离线方法, 在设置其接收/已读时间
    /// TODO 此命令功能有待加强, 客户端有自动断线重连的能力, 不会主动退出, 因此如果要求客户端退出, 需要客户端配置
    /// </summary>
    /// <param name="message">退出的消息</param>
    /// <returns></returns>
    public virtual async Task Abort(Message message)
    {
        try
        {
            if (string.IsNullOrWhiteSpace(message.Receiver))
            {
                _logger.LogWarning($"Server received a Abort message from {User} in {Client} but no message receiver!");
                return;
            }

            InitMessage(message);
            message.ReceiveTime = DateTime.UtcNow;
            message.Type = MessageType.Abort;
            message.Content = nameof(Abort);

            //退出指定客户端
            await Clients.Group(message.Receiver).Abort();

            //记录日志
            LogMessage(message);

            //保存当前消息
            await SaveMessageAsync(message);
        }
        catch (Exception ex)
        {
            LogException(ex, nameof(Abort));
        }
    }
    /// <summary>
    /// 服务器接收心跳消息, 心跳消息为客户端保活信号
    /// </summary>
    /// <param name="message"></param>
    /// <returns></returns>
    public virtual async Task Heartbeat()
    {
        try
        {
            if (_hostSetting.SaveHeartbeat)
            {
                var message = InitMessage(new Message
                {
                    Id = nameof(Heartbeat) + AppSettings.CHAR_UNDERLINE + User,
                    Type = MessageType.Heartbeat,
                    Content = MessageType.Heartbeat.ToString(),
                    CreateTime = DateTime.UtcNow
                });

                //LogMessage(message);
                await SaveMessageAsync(message);
            }

            _logger.LogInformation("Server receive the Heartbeat from user {User} in client {Client}", User, Client);
        }
        catch (Exception ex)
        {
            LogException(ex, nameof(Heartbeat));
        }
    }
    /// <summary>
    /// 获取在线用户列表
    /// </summary>
    /// <returns></returns>
    public virtual async Task<Message> GetOnlineUsers()
    {
        var users = await _cache.GetValueAsync<List<string>>(AppSettings.CACHE_APPLICATION + CACHE_ONLINE_USERS) ?? new List<string>();
        var message = InitMessage(new Message
        {
            Type = MessageType.Initial,
            Receiver = User,
            Sender = AppSettings.USER_SYSTEM,
            SendTime = DateTime.UtcNow,
            Content = users.ToString<string>()
        });
        //LogMessage(message);
        //await SaveMessageAsync(message);
        //await base.SendAsync(message, User);
        _logger.LogInformation("Server receive the {method} request from user {User} in client {Client}, the online users are: " + message.Content, nameof(GetOnlineUsers), User, Client);
        return message;
    }
    /// <summary>
    /// 服务器接收指令
    /// </summary>
    /// <param name="message"></param>
    public virtual void OnlineNotice(Message message)
    {
        if (message.Type == MessageType.On)
        {
            _hostSetting.OnlineNotice = true;
            _hostSetting.OfflineNotice = true;
            _hostSetting.NoticeReceiver = User;
        }
        else
        {
            _hostSetting.OnlineNotice = false;
            _hostSetting.OfflineNotice = false;
        }
    }
    #endregion

    #region 内部成员
    /// <summary>
    /// 上线通知
    /// </summary>
    /// <returns></returns>
    protected override async Task OnlineAsync()
    {
        await base.OnlineAsync();
        if (_hostSetting.OfflineNotice && User != _hostSetting.NoticeReceiver)
        {
            await SendAsync(InitMessage(new Message { Type = MessageType.Online, Content = User, Sender = AppSettings.USER_SYSTEM, Receiver = _hostSetting.NoticeReceiver }), _hostSetting.NoticeReceiver);
            _logger.LogInformation("User {user} in client {client} has been connected and will send online notice to {noticeReceiver}", User, Client, _hostSetting.NoticeReceiver);
        }
    }
    /// <summary>
    /// 离线通知
    /// </summary>
    /// <returns></returns>
    protected override async Task OfflineAsync()
    {
        await base.OfflineAsync();
        if (_hostSetting.OfflineNotice && User != _hostSetting.NoticeReceiver)
        {
            await SendAsync(InitMessage(new Message { Type = MessageType.Offline, Content = User, Sender = AppSettings.USER_SYSTEM, Receiver = _hostSetting.NoticeReceiver }), _hostSetting.NoticeReceiver);
            _logger.LogInformation("User {user} in client {client} has been disconnected and will send offline notice to {noticeReceiver}", User, Client, _hostSetting.NoticeReceiver);
        }
    }
    /// <summary>
    /// 记录消息日志
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="message"></param>
    protected virtual void LogMessage(Message message)
    {
        if (message.Content == null && string.IsNullOrWhiteSpace(message.Receiver))
        {
            _logger.LogInformation("Server receive the {messageType} from user {user} in client {client} with message id is: " + message.Id, message.Type, message.Sender, message.From);
        }
        else
        {
            var receiver = message.Group ?? string.Empty;
            receiver += (receiver.IsNullOrWhiteSpace() ? string.Empty : "@") + (message.Receiver ?? Receiver);
            _logger.LogInformation(LOG_INFO + message.Content, message.Type, message.Sender, message.From, receiver);
        }
    }
    /// <summary>
    /// 记录异常
    /// </summary>
    /// <param name="ex"></param>
    /// <param name="method"></param>
    protected virtual void LogException(Exception ex, string method) => _logger.LogError(ex, "Server receive the {messageType} message from user {user} in client {client} throw exception", method, User, Client);
    /// <summary>
    /// 记录异常
    /// </summary>
    /// <param name="ex"></param>
    /// <param name="message"></param>
    protected virtual void LogException(Exception ex, Message message) => _logger.LogError(ex, LOG_INFO + message.Content + " throw exception", message.Type, message.Sender, message.From, message.Receiver);
    /// <summary>
    /// 从缓存中获取消息
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="message"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    protected virtual async Task<Message> GetFromCacheAsync(string key)
    {
        var message = await _cache.GetValueAsync<Message>(key);
        if (message == null)
        {
            throw new Exception($"The message of id {key} not found!");
        }

        return message;
    }
    /// <summary>
    /// 保存消息
    /// </summary>
    /// <param name="message"></param>
    /// <returns></returns>
    protected virtual async Task SaveMessageAsync(Message message)
    {
        await _cache.SetValueAsync(message.Id, message);
    }
    /// <summary>
    /// 初始化填充消息
    /// </summary>
    /// <param name="message"></param>
    protected virtual Message InitMessage(Message message)
    {
        //填充消息编号和消息当前连接
        message.Id ??= KeyGenerator.GetKey<string>();
        message.Sender ??= User;
        message.SendTime = DateTime.UtcNow;
        message.From ??= Client;
        return message;
    }
    #endregion
}